﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ProtoBuf;
using SharpDX;

namespace Engine
{
    /// <summary>
    /// Класс камеры
    /// </summary>
    [ProtoContract]
    public class EEDCamera 
    {
        /// <summary>
        /// Фокусное расстояние
        /// </summary>
        [ProtoMember(1)]
        public float fov;

        /// <summary>
        /// позиция камеры
        /// </summary>
        [ProtoMember(2)]
        public DVector3 position;

        /// <summary>
        /// UP Vector
        /// </summary>
        [ProtoMember(3)]
        public Vector3 up_vector;

        /// <summary>
        /// кватернион камеры
        /// </summary>
        [ProtoMember(4)]
        public Quaternion quaternion;

        /// <summary>
        /// Расстояние до ближней плоскости отсечения
        /// </summary>
        [ProtoMember(5)]
        public float nearClip;

        /// <summary>
        /// Расстояние до дальней плоскости отсечения
        /// </summary>
        [ProtoMember(6)]
        public float farClip;

        /// <summary>
        /// Отношение ширины к высоте
        /// </summary>
        [ProtoMember(7)]
        public float aspectRatio;

        /// <summary>
        /// Матрица вида
        /// </summary>
        public Matrix viewMatrix;

        /// <summary>
        /// Направление камеры
        /// </summary>
        [ProtoMember(8)]
        public Vector3 direction;

        /// <summary>
        /// Матрица проекции
        /// </summary>
        public Matrix projectionMatrix;

        /// <summary>
        /// Инвертированная матрица вида
        /// </summary>
        public Matrix invertViewMatrix;

        /// <summary>
        /// Матрица вида проекции (viewMatrix*projectionMatrix)
        /// </summary>
        public Matrix viewProjectionMatrix;

        //public Matrix InvertViewProjectionMatrix;

        /// <summary>
        /// Трапеция видимости
        /// </summary>
        public BoundingFrustum cameraFrustum, LcameraFrustum;

        protected float yFac = 0;
        protected float xFac = 0;

        public DVector3 FarT1, FarT2, FarT3, FarT4;

        public EEDCamera() { }

        /// <param name="ScrX">Ширина экрана</param>
        /// <param name="ScrY">Высота экрана</param>
        /// <param name="Fov">Фокусное растояние</param>
        /// <param name="NearClip">Ближняя плоскость отсечения</param>
        /// <param name="FarClip">Дальняя плоскость отсечения</param>
        /// <param name="game">Класс игры</param>
        public EEDCamera(int ScrX, int ScrY, float Fov, float NearClip, float FarClip)
        {
            aspectRatio = ((float)ScrX / (float)ScrY);
            fov = Utils.Deg2Rad(Fov);
            nearClip = NearClip;
            farClip = FarClip;
        }


        /// <summary>
        /// Обновление камеры
        /// </summary>
        public void Update()
        {
            Vector3 tmp_vec = new Vector3(0, 1, 0);//Up Vector
            up_vector = Vector3.Transform(tmp_vec, quaternion);

            tmp_vec = new Vector3(0, 0, -1);//Front Vector
            direction = Vector3.Transform(tmp_vec, quaternion);

            viewMatrix = Matrix.LookAtRH(new Vector3(), direction, up_vector);

            projectionMatrix = Matrix.PerspectiveFovRH(fov, aspectRatio, nearClip, farClip);

            viewProjectionMatrix = viewMatrix * projectionMatrix;

            LcameraFrustum = new BoundingFrustum(viewProjectionMatrix);

            cameraFrustum = new BoundingFrustum(Matrix.LookAtRH(Conversion.ToVector3(position), Conversion.ToVector3(position) + direction, up_vector) * projectionMatrix);

            invertViewMatrix = Matrix.Invert(viewMatrix);

            //InvertViewProjectionMatrix = Matrix.Invert(viewProjectionMatrix);

            yFac = (float)Math.Tan(fov / 2f);
            xFac = yFac * aspectRatio;

            FarT1 = position + Conversion.ToDoubleVector(new Vector3(-invertViewMatrix.M31, -invertViewMatrix.M32, -invertViewMatrix.M33)) * farClip
                - farClip * Conversion.ToDoubleVector(new Vector3(invertViewMatrix.M11, invertViewMatrix.M12, invertViewMatrix.M13)) * xFac
                - Conversion.ToDoubleVector(new Vector3(invertViewMatrix.M21, invertViewMatrix.M22, invertViewMatrix.M23)) * yFac * farClip;
            FarT2 = position + Conversion.ToDoubleVector(new Vector3(-invertViewMatrix.M31, -invertViewMatrix.M32, -invertViewMatrix.M33)) * farClip
                - farClip * Conversion.ToDoubleVector(new Vector3(invertViewMatrix.M11, invertViewMatrix.M12, invertViewMatrix.M13)) * xFac
                + Conversion.ToDoubleVector(new Vector3(invertViewMatrix.M21, invertViewMatrix.M22, invertViewMatrix.M23)) * yFac * farClip;
            FarT3 = position + Conversion.ToDoubleVector(new Vector3(-invertViewMatrix.M31, -invertViewMatrix.M32, -invertViewMatrix.M33)) * farClip
                + farClip * Conversion.ToDoubleVector(new Vector3(invertViewMatrix.M11, invertViewMatrix.M12, invertViewMatrix.M13)) * xFac
                + Conversion.ToDoubleVector(new Vector3(invertViewMatrix.M21, invertViewMatrix.M22, invertViewMatrix.M23)) * yFac * farClip;
            FarT4 = position + Conversion.ToDoubleVector(new Vector3(-invertViewMatrix.M31, -invertViewMatrix.M32, -invertViewMatrix.M33)) * farClip
                + farClip * Conversion.ToDoubleVector(new Vector3(invertViewMatrix.M11, invertViewMatrix.M12, invertViewMatrix.M13)) * xFac
                - Conversion.ToDoubleVector(new Vector3(invertViewMatrix.M21, invertViewMatrix.M22, invertViewMatrix.M23)) * yFac * farClip;
        }
    }
}
